package org.vacoor.xqq.ui.comp.tree;

import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 实现监控 {@link org.vacoor.xqq.ui.comp.tree.IdentifiableAndObservableDataNode} 变化, 即时更新
 * <p/>
 *
 * @author: Vacoor
 */
public class DataNodeMonitorTreeModel extends DefaultTreeModel {
    private Map<Long, IdentifiableAndObservableDataNode> identifiableChildren = new ConcurrentHashMap<Long, IdentifiableAndObservableDataNode>();
    private Monitor monitor;

    public DataNodeMonitorTreeModel(IdentifiableAndObservableDataNode root) {
        this(root, false);
    }

    public DataNodeMonitorTreeModel(IdentifiableAndObservableDataNode root, boolean asksAllowsChildren) {
        super(root, asksAllowsChildren);
        ensureHandler();
        installMonitor(root);
    }

    private void installMonitor(TreeNode root) {
        IdentifiableAndObservableDataNode n = getIdentifiableAndObservableNode(root);
        if (n != null) {
            identifiableChildren.put(n.getId(), n);
            n.addPropertyChangeListener(monitor);
        }
        for (int i = 0; i < root.getChildCount(); i++) {
            installMonitor(root.getChildAt(i));
        }
    }

    public IdentifiableAndObservableDataNode getIdentifiableAndObservableNode(long id) {
        return identifiableChildren.get(id);
    }

    @Override
    public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) {
        IdentifiableAndObservableDataNode n = getIdentifiableAndObservableNode(newChild);
        if (n != null) {
            identifiableChildren.put(n.getId(), n);
            n.addPropertyChangeListener(monitor);
        }
        super.insertNodeInto(newChild, parent, index);
    }

    @Override
    public void removeNodeFromParent(MutableTreeNode node) {
        IdentifiableAndObservableDataNode n = getIdentifiableAndObservableNode(node);
        if (n != null) {
            identifiableChildren.remove(n.getId());
//            n.removePropertyChangeListener(monitor);
            for (PropertyChangeListener l : n.getPropertyChangeListeners()) {
                n.removePropertyChangeListener(l);
            }
        }
        super.removeNodeFromParent(node);
    }

    private IdentifiableAndObservableDataNode getIdentifiableAndObservableNode(TreeNode node) {
        if (!(node instanceof IdentifiableAndObservableDataNode) || node == null) {
            return null;
        }
        return (IdentifiableAndObservableDataNode) node;
    }

    /**
     * 节点刷新操作, 该方法会触发 JTree 重绘不需再次调用
     * 如果仅仅是需要重绘, 则应该使用 {@link javax.swing.JTree#repaint(java.awt.Rectangle)}, 效率要比该方法高
     */
    protected void refreshNode(final TreeNode node) {
        if (node == null) {
            return;
        }
        nodeChanged(node);
    }

    /**
     * 节点结构刷新操作, 该方法会触发 JTree 重绘不需再次调用
     * 如果仅仅是需要重绘, 则应该使用 {@link javax.swing.JTree#repaint(java.awt.Rectangle)}, 效率要比该方法高
     */
    protected void refreshSructure(final TreeNode node) {
        if (node == null) {
            return;
        }
        // 在 AWTThread 中执行
        // SwingUtilities.invokeLater(new Runnable() {
        //    public void run() {
        nodeStructureChanged(node);
        //    }
        // });
    }

    private void ensureHandler() {
        if (monitor != null) {
            return;
        }
        synchronized (DataNodeMonitorTreeModel.class) {
            if (monitor == null) {
                monitor = new Monitor();
            }
        }
    }

    protected class Monitor implements PropertyChangeListener {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            Object source = evt.getSource();
            if (!(source instanceof TreeNode)) {
                return;
            }
            refreshNode((TreeNode) source);
        }
    }
}
